#include"redis.hpp"
#include"json.hpp"

#include<iostream>
#include<fstream>

using namespace std;
using json = nlohmann::json;

Redis::Redis():_publish_context(nullptr), _subscribe_context(nullptr){
}

Redis::~Redis(){
    if(_publish_context != nullptr)redisFree(_publish_context);
    if(_subscribe_context != nullptr)redisFree(_subscribe_context);
}

bool Redis::connect(){
    //读取json配置文件
    json js;

	ifstream jfile("../src/server/redis/redisConfig.json");
	if(!jfile.good()){
		cerr << "数据库连接池配置文件不存在" << endl;
		return false;
	}
	jfile >> js;
	
	string ip = js["ip"];
	unsigned short port = js["port"];

    //负责publish发布消息的上下文连接
    _publish_context = redisConnect(ip.c_str(), port);
    if(_publish_context == nullptr){
        cerr << "publish context connect error!" <<endl;
        return false;
    }

    //负责publish发布消息的上下文连接
    _subscribe_context = redisConnect(ip.c_str(), port);
    if(_subscribe_context == nullptr){
        cerr << "subscribe context connect error!" <<endl;
        return false;
    }
	
    //在单独的线程中监听通道事件，有消息往业务层上报
    thread t(&Redis::observer_channel_message, this);
    t.detach();

    cout<< "connect redis-server success!" << endl;
    return true;
}

//向redis指定的通道channel发布消息
bool Redis::publish(int channel, string message){
    redisReply* reply = (redisReply*)redisCommand(_publish_context, "PUBLISH %d %s", channel, message);
    if(reply == nullptr){
        cerr << "publish command failed!" <<endl;
        return false;
    }
    freeReplyObject(reply);
    return true;
}

//向redis指定的通道subscribe消息
bool Redis::subscribe(int channel){
    //redisCommand实际上调用了redisAppendCommand、redisBufferWrite、redisBufferRead
    //而订阅命令如果使用redisBufferRead会阻塞等待消息回复，所以这里不采用
    if(REDIS_ERR == redisAppendCommand(_subscribe_context, "SUBSCRIBE %d", channel)){
        cerr << "subscribe command failed!" <<endl;
        return false;
    }
    //redisBufferWrite可以循环发送，直到flag被置为1时停止
    int flag = 0;
    while(!flag){
        if(REDIS_ERR == redisBufferWrite(_subscribe_context, &flag)){
            cerr << "subscribe command failed!" <<endl;
            return false;
        }
    }

    return true;
}

//向redis指定的通道unsubscribe消息
bool Redis::unsubscribe(int channel){
    //redisCommand实际上调用了redisAppendCommand、redisBufferWrite、redisBufferRead
    //而订阅命令如果使用redisBufferRead会阻塞等待消息回复，所以这里不采用
    if(REDIS_ERR == redisAppendCommand(_subscribe_context, "UNSUBSCRIBE %d", channel)){
        cerr << "unsubscribe command failed!" <<endl;
        return false;
    }
    //redisBufferWrite可以循环发送，直到flag被置为1时停止
    int flag = 0;
    while(!flag){
        if(REDIS_ERR == redisBufferWrite(_subscribe_context, &flag)){
            cerr << "unsubscribe command failed!" <<endl;
            return false;
        }
    }

    return true;
}

//在独立的线程中接收订阅通道中的消息
void Redis::observer_channel_message(){
    redisReply *reply = nullptr;
    //循环阻塞取消息
    while(REDIS_OK == redisGetReply(_subscribe_context, (void**)&reply)){
        //订阅的消息是一个三元组的数组
        if(reply != nullptr && reply->element[2] != nullptr && reply->element[2]->str != nullptr){
            //调用预先设置好的消息回调函数
            _notify_message_handler(reply->element[1]->integer, reply->element[2]->str);
        }

        freeReplyObject(reply);
    }

    cerr << "Redis::observer_channel_message() exit" <<endl;
}

//初始化向业务层上报通道消息的回调对象
void Redis::init_notify_handler(function<void(int,string)> func){
    _notify_message_handler = func;
}

